package model

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	config "gitee.com/lanshiren/miniprogram/app/conf"
)

const (
	Admin      = 1
	Editor     = 2
	Leader     = 3
	SimpleUser = 9
	BlackUser  = 10
)

type UsersDO struct {
	ID           uint `gorm:"primary_key"`
	UpdatedAt    time.Time
	CreatedAt    time.Time
	Name         string
	Nickname     string
	Gender       uint
	City         string
	AvatarUrl    string
	Campus       string
	Phone        string
	Major        string
	WeId         string
	OpenId       string
	RoleId       int
	BanStatus    int
	LastBanPoint time.Time
	BanTime      int
}

type UserVO struct {
	ID        uint   `json:"id"`
	Name      string `json:"name"`
	Nickname  string `json:"nickName"`
	Gender    uint   `json:"gender"`
	City      string `json:"city"`
	AvatarUrl string `json:"avatarUrl"`
	Campus    string `json:"campus"`
	Phone     string `json:"phone"`
	RoleId    int    `json:"roleId"`
	BanStatus int    `json:"ban_status"`
}

type SearchOneUserParam struct {
	SearchKey string `json:"search_key" form:"search_key"`
	Role      int    `json:"role" form:"role"`
}

type UpdateUserRoleParam struct {
	ContactID int `json:"contact_id" form:"contact_id"`
	NewRole   int `json:"new_role" form:"new_role"`
}

type SearchUsersParam struct {
	Page     string `json:"page" form:"page"`
	PageSize string `json:"pagesize" form:"pagesize"`
	Name     string `json:"name" form:"name"`
	Phone    string `json:"phone" form:"phone"`
	RoleId   int    `json:"role_id" form:"role_id"`
}

type UpdateUserParam struct {
	//OpenID    string `json:"open_id"`
	//WeID      uint   `json:"we_id"`
	//Name      string `json:"name"`
	Phone string `json:"phone" form:"phone"`
	//Major     string `json:"major"`
	NickName  string `json:"nickName" form:"nickName"`
	AvatarUrl string `json:"avatarUrl"`
	//Gender    uint   `json:"gender"`
	//BanStatus uint   `json:"ban_status"`
}

func (UsersDO) TableName() string {
	return "users"
}

func CreateUser(user *UsersDO) error {
	db := config.GetDB()
	u := UsersDO{}
	db.Where("open_id = ?", user.OpenId).First(&u)
	if u.OpenId != "" {
		return errors.New(fmt.Sprintf("user %s exists!", u.OpenId))
	}
	if err := db.Save(user).Error; err != nil {
		return err
	}
	return nil
}

func QueryAndUpdateUser(openId string, user *UsersDO) error {
	db := config.GetDB()
	u := UsersDO{}
	err := db.Where("open_id = ?", openId).First(&u).Error
	if err != nil {
		return err
	} else {
		if u.Name != "" && user.Name == "" {
			user.Name = u.Name
		}
		if u.ID != 0 {
			err := db.Model(&u).Updates(
				map[string]interface{}{
					"nickname":   user.Nickname,
					"name":       user.Name,
					"gender":     user.Gender,
					"avatar_url": user.AvatarUrl,
					"phone":      user.Phone,
					"role_id":    user.RoleId,
					"city":       user.City,
				}).Error
			if err != nil {
				return err
			}
		}
	}
	return err
}

func QueryAndUpdateUserNameAndPhone(openId string, user *UsersDO) error {
	db := config.GetDB()
	u := UsersDO{}
	err := db.Where("open_id = ?", openId).First(&u).Error
	if err != nil {
		return err
	} else {
		if u.Name != "" && user.Name == "" {
			user.Name = u.Name
		}
		if u.ID != 0 {
			err := db.Model(&u).Updates(
				map[string]interface{}{
					"nickname":   user.Nickname,
					"phone":      user.Phone,
					"avatar_url": user.AvatarUrl,
				}).Error
			if err != nil {
				return err
			}
		}
	}
	return err
}

func QueryAndUpdateUserRole(contactId int, newRole int) error {
	db := config.GetDB()
	u := UsersDO{}
	err := db.Where("id = ?", contactId).First(&u).Error
	if err != nil {
		return err
	} else {
		if u.ID != 0 {
			err := db.Model(&u).Updates(
				map[string]interface{}{
					"role_id": newRole,
				}).Error
			if err != nil {
				return err
			}
		}
	}
	return err
}

func QueryUser(openId string) (UsersDO, error) {
	db := config.GetDB()
	u := UsersDO{}
	err := db.Where("open_id = ?", openId).First(&u).Error
	if err != nil {
		return UsersDO{}, err
	}
	return u, nil
}

func QueryUserByID(id uint) (UsersDO, error) {
	db := config.GetDB()
	u := UsersDO{}
	err := db.Where("id = ?", id).First(&u).Error
	if err != nil {
		return UsersDO{}, err
	}
	return u, nil
}

func GetUserByNikeNameAndRole(nikeName string, roleId int) (UsersDO, error) {
	db := config.GetDB()
	u := UsersDO{}
	var err error
	if roleId == 0 {
		err = db.Where("nickname = ?", nikeName).First(&u).Error
	} else {
		err = db.Where("nickname = ? and role_id = ?", nikeName, roleId).First(&u).Error
	}
	if err != nil {
		return UsersDO{}, err
	}
	return u, nil
}

func GetUserByPhoneAndRole(phone string, roleId int) (UsersDO, error) {
	db := config.GetDB()
	u := UsersDO{}
	var err error
	if roleId == 0 {
		err = db.Where("phone = ?", phone).First(&u).Error
	} else {
		err = db.Where("phone = ? and role_id = ?", phone, roleId).First(&u).Error
	}
	if err != nil {
		return UsersDO{}, err
	}
	return u, nil
}

func SearchContactsList(param SearchUsersParam) (total int, contacts []*UsersDO, err error) {
	db := config.GetDB()
	page, err := strconv.Atoi(param.Page)
	if err != nil {
		return 0, nil, err
	}
	pageSize, err := strconv.Atoi(param.PageSize)
	if err != nil {
		return 0, nil, err
	}
	offset := page * pageSize
	limit := param.PageSize
	clauseKeys := []string{}
	clauseValues := []interface{}{}
	if len(param.Name) != 0 {
		clauseKeys = append(clauseKeys, "users.name LIKE ?")
		clauseValues = append(clauseValues, fmt.Sprintf(`%%%s%%`, param.Name))
	}
	if len(param.Phone) != 0 {
		clauseKeys = append(clauseKeys, "users.phone LIKE ?")
		clauseValues = append(clauseValues, fmt.Sprintf(`%%%s%%`, param.Phone))
	}
	if param.RoleId != 0 {
		clauseKeys = append(clauseKeys, "users.role_id = ?")
		clauseValues = append(clauseValues, param.RoleId)
	}
	if len(clauseKeys) > 0 {
		err := db.Table("users").Select("users.*").Where(
			strings.Join(clauseKeys, " AND "), clauseValues...).Count(&total).Error
		if err != nil {
			return total, nil, err
		}
		err = db.Table("users").Select("users.*").
			Where(strings.Join(clauseKeys, " AND "), clauseValues...).Order("users.id DESC").
			Limit(limit).Offset(offset).Scan(&contacts).Error
	} else {
		err := db.Table("users").Select("users.*").Count(&total).Error
		if err != nil {
			return total, nil, err
		}
		err = db.Table("users").Select("users.*").
			Order("users.id DESC").
			Limit(limit).Offset(offset).Scan(&contacts).Error
	}
	return total, contacts, nil
}
